import eventlet
eventlet.monkey_patch()
from flask import Flask, request, jsonify, render_template, send_from_directory, Response, session, redirect, url_for
import os
from fire_smoke_detection import Detection
from flask_socketio import SocketIO, emit
from werkzeug.middleware.proxy_fix import ProxyFix
app = Flask(__name__)
socketio = SocketIO(app,
                    # cors_allowed_origins='*',
                    # logger=True,
                    # engineio_logger=True,
                    async_mode='eventlet',
                    # message_queue='redis://localhost:6379/0',
                    ping_interval = 25,  # 发送心跳间隔（秒）
                    ping_timeout = 5,  # 心跳超时时间（秒）
                    max_http_buffer_size = 1e8  # 增加最大消息缓冲区
                    )
# 配置代理头解析（信任 1 层代理）
app.wsgi_app = ProxyFix(
    app.wsgi_app,
    x_for=1,      # 解析 X-Forwarded-For 头
    x_proto=1,    # 解析 X-Forwarded-Proto 头
    x_host=1,     # 解析 X-Forwarded-Host 头
    x_port=1      # 解析 X-Forwarded-Port 头
)
app.secret_key = 'flaskflask666'
UPLOAD_FOLDER = os.path.join('uploads')
PROCESSED_FOLDER = os.path.join('processed')
CONFIG = os.path.join('config')
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)
if not os.path.exists(PROCESSED_FOLDER):
    os.makedirs(PROCESSED_FOLDER)
if not os.path.exists(CONFIG):
    os.makedirs(CONFIG)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['PROCESSED_FOLDER'] = PROCESSED_FOLDER
app.config['CONFIG'] = CONFIG
# 允许上传的图片和视频类型
ALLOWED_IMAGE_EXTENSIONS = {'jpg', 'jpeg', 'png', 'gif'}
ALLOWED_VIDEO_EXTENSIONS = {'mp4', 'avi', 'mov'}
count = 0

def allowed_file(filename, allowed_extensions):
    """检查文件扩展名是否允许"""
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_extensions

def process_client_ip(addr,count):
    return addr + f":{count}"
def confirm_emit_status(status):
    print(f"消息发送状态：{status}")
def process_path(ip,filename):
    upload_path = os.path.join(app.config['UPLOAD_FOLDER'],ip,filename)
    processed_path = os.path.join(app.config['PROCESSED_FOLDER'],ip,filename)
    if not os.path.exists(os.path.join(app.config['UPLOAD_FOLDER'],ip)):
        os.mkdir(os.path.join(app.config['UPLOAD_FOLDER'],ip))
    if not os.path.exists(os.path.join(app.config['PROCESSED_FOLDER'],ip)):
        os.mkdir(os.path.join(app.config['PROCESSED_FOLDER'],ip))
    return upload_path, processed_path
def exists(ip,filename):
    return os.path.exists(os.path.join(app.config['UPLOAD_FOLDER'],ip,filename)) and os.path.exists(os.path.join(app.config['PROCESSED_FOLDER'],ip,filename))
from utils import MediaComparator,read_json_file
mediaComparator = MediaComparator()
detects = {}
def process_redis_key(ip):
    return f'detect-{ip}'
# 辅助函数：获取或创建 detect 实例
def get_detect_instance(ip):
    if ip not in detects:
        detects[ip] = Detection()
    return detects[ip]

@app.route('/')
def index():
    return render_template("index.html")
@socketio.on('connect')
def handle_connect():
    # 获取客户端的 IP 地址作为标识
    client_ip = request.remote_addr
    detect_instance = get_detect_instance(client_ip)
    # 获取客户端的会话 ID
    client_sid = request.sid
    print(detect_instance)
    if detect_instance:
        print(f"Client {client_ip} connected with session ID: {client_sid},为这个ip创建一个detect实例")
@socketio.on('disconnect')
def handle_disconnect():
    # 获取客户端的 IP 地址作为标识
    client_ip = request.remote_addr
    if client_ip in detects:
        detects.pop(client_ip)
    print(f"Client {client_ip} disconnected,为这个ip删除detect实例")
@app.route('/upload/image', methods=['POST'])
def upload_image():
    """处理图片上传"""
    if 'image' not in request.files:
        return jsonify({'error': '未找到图片文件'}), 400
    ip = request.remote_addr
    detect = get_detect_instance(ip)
    file = request.files['image']
    if file.filename == '':
        return jsonify({'error': '未选择图片文件'}), 400
    if exists(ip, file.filename):
        print("同名文件已存在")
        temp_path = os.path.join(".", "temp_files", ip, file.filename)
        if not os.path.exists(os.path.join(".", "temp_files", ip)):
            os.mkdir(os.path.join(".", "temp_files", ip))

        file.save(temp_path)
        file_path,save_path = process_path(ip,file.filename)
        print(f"对比文件：{temp_path}vs{save_path}")
        result = mediaComparator.compare(temp_path,save_path)
        print(f"匹配结果：{result}")
        # os.remove(temp_path)
        if result['match']:
            print("匹配度足够，认为是同一文件")
            return send_from_directory(os.path.join(app.config['PROCESSED_FOLDER'], ip), file.filename)
        else:
            print("匹配度不够，重新生成")
    if file and allowed_file(file.filename, ALLOWED_IMAGE_EXTENSIONS):
        # filename = secure_filename(file.filename)  #会过滤掉中文
        filename = file.filename
        print(f"filename:{filename}")
        file_path,save_path = process_path(request.remote_addr,filename)
        file.seek(0)
        file.save(file_path)
        # 都已经保存了，直接处理保存的文件就好了呀
        print(file_path, save_path)
        processed_file = detect.handle_image_input(file_path,save_path)
        print("处理文件结束，保存")
        print(UPLOAD_FOLDER, filename)
        return send_from_directory(os.path.join(app.config['PROCESSED_FOLDER'],request.remote_addr), filename)
    return jsonify({'error': '只允许上传 JPEG、PNG 或 GIF 格式的图片！'}), 400

@app.route('/upload/video', methods=['POST'])
def upload_video():
    """处理视频上传"""
    if 'video' not in request.files:
        return jsonify({'error': '未找到视频文件'}), 400
    file = request.files['video']
    client_ip = request.remote_addr

    if file.filename == '':
        return jsonify({'error': '未选择视频文件'}), 400
    if exists(client_ip, file.filename):
        print("同名文件已存在")
        temp_path = os.path.join(".","temp_files",client_ip,file.filename)
        if not os.path.exists(os.path.join(".","temp_files",client_ip)):
            os.mkdir(os.path.join(".","temp_files",client_ip))
        file.save(temp_path)
        file_path,save_path = process_path(client_ip,file.filename)
        result = mediaComparator.compare(temp_path,save_path)
        print(f"对比结果：{result}")
        os.remove(temp_path)
        if result['match']:
            print("匹配度足够，认为是同一文件。")
            return jsonify({'message':'视频已存在','match':result['match'],'filename':file.filename}),200
        else:
            print("匹配度不足。")
    if file and allowed_file(file.filename, ALLOWED_VIDEO_EXTENSIONS):
        # filename = secure_filename(file.filename)  #会过滤掉中文
        filename = file.filename
        file_path,save_path = process_path(client_ip,filename)
        file.seek(0)
        file.save(file_path)
        return jsonify({'message': '视频上传并处理成功','filename':filename}), 200
    else:
        return jsonify({'error': '只允许上传 MP4、AVI 或 MOV 格式的视频！'}), 400
@socketio.on("process_and_show_video_frame")
def process_and_show_video_frame(filename):
    print(request.remote_addr, detects)
    detect = get_detect_instance(request.remote_addr)
    if request.remote_addr:
        file_path, save_path = process_path(request.remote_addr, filename)
    else:
        # 供测试使用
        file_path, save_path = process_path("127.0.0.1-client", filename)
    # false用来测试，不添加用来运行。
    for data in detect.process_video_frame(file_path, save_path,out_put=True):
        print(f"本次处理视频帧的是{request.remote_addr}的{request.sid},向他发送一帧。")
        emit("processed_video_frame", data, callback=confirm_emit_status)
        # 非常重要，否则emit会累计完再发送，导致不可预测的问题。
        eventlet.sleep(0)

@app.route('/download/video', methods=['POST'])
def download_video():
    """处理视频上传"""
    filename = request.form.get('filename')
    return send_from_directory(os.path.join(app.config['PROCESSED_FOLDER'],request.remote_addr), filename)

@socketio.on('video_output_stop')
def video_output_stop():
    detect = get_detect_instance(request.remote_addr)
    print("尝试中止视频生成")
    # session_stop_set()
    detect.video_output_stop()

@socketio.on('camera_frame')
def handle_camera_frame(data):
    detect = get_detect_instance(request.remote_addr)
    print("处理摄像头帧",data.keys())
    frame = data['frame']
    deviceId = data['deviceId']
    try:
        processed_data = detect.process_camera_frame(frame)
        processed_data['deviceId'] = deviceId
        print("返回帧：",processed_data.keys())

        # 发送处理后的图像数据回前端
        emit('processed_camera_frame', processed_data)
        eventlet.sleep(0)
    except Exception as e:
        print(f"处理视频帧时出错: {e}")
# methods一定要有，否则报错405
@app.route('/upload/config',methods=['POST'])
def upload_config():
    print("开始上传配置文件")
    config_file = request.files['config']
    if not config_file:
        return jsonify({
            'success': False,
            'message': '未上传文件'
        }), 400
    if config_file.filename.split('.')[-1].lower() != 'json':
        return jsonify({
            'success': False,
            'message': '请上传JSON格式的文件'
        }), 400
    try:
        ip = request.remote_addr
        # 获取文件，然后保存。然后读取，并返回内容
        save_path = os.path.join(app.config['CONFIG'], ip)
        if not os.path.exists(save_path):
            os.mkdir(save_path)
        config_file.seek(0)
        file_path = os.path.join(save_path,"config.json")
        config_file.save(file_path)
        print(f"配置文件保存成功！{file_path}")
        return jsonify({
            'success': True,
            'message': '文件上传成功'
        })
    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'上传失败: {str(e)}'
        }), 500

@socketio.on('get_config')
def get_config():
    ip = request.remote_addr
    config_path = os.path.join(app.config['CONFIG'], ip,"config.json")
    data = read_json_file(config_path)
    print(data)
    emit('config',data)

if __name__ == '__main__':
    socketio.run(app,
                 debug=True,
                 host='0.0.0.0',
                 port=5000,
                 # ssl_context=('cert.pem','key.pem'),
                 certfile='./signature/cert.pem',
                 keyfile='./signature/key.pem',
                 )